<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="IHze1" id="IHze1"><span data-lake-id="u02686424" id="u02686424">典型回答</span></h1>
  <p data-lake-id="u15a8396d" id="u15a8396d"><br></p>
  <p data-lake-id="uc156c3c7" id="uc156c3c7"><span data-lake-id="u97c26f92" id="u97c26f92">因为不是所有的小数都能用二进制表示（扩展知识中介绍为啥不能表示），所以，为了解决这个问题，IEEE</span><strong><span data-lake-id="u6a5c27da" id="u6a5c27da">提出了一种使用近似值表示小数的方式，并且引入了精度的概念。这就是我们所熟知的浮点数。</span></strong></p>
  <p data-lake-id="u7f9c4404" id="u7f9c4404"><strong><span data-lake-id="u030b3daa" id="u030b3daa">​</span></strong><br></p>
  <p data-lake-id="u431edc85" id="u431edc85"><span data-lake-id="ued20c1f1" id="ued20c1f1">比如0.1+0.2 != 0.3，而是等于0.30000000000000004 （甚至有一个网站就叫做 </span><a href="https://0.30000000000000004.com/" target="_blank" data-lake-id="udee80725" id="udee80725"><span data-lake-id="u7bf5ce17" id="u7bf5ce17">https://0.30000000000000004.com/</span></a><span data-lake-id="u981451be" id="u981451be"> ，就是来解释这个现象的）</span></p>
  <p data-lake-id="ud8dd392a" id="ud8dd392a"><span data-lake-id="ua3c5c7ff" id="ua3c5c7ff">​</span><br></p>
  <p data-lake-id="u405d5a58" id="u405d5a58"><img src="https://cdn.nlark.com/yuque/0/2024/png/5378072/1705323582322-db6091dd-8ba3-4249-8ba3-5fe8388f7f92.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_10%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u7ad72aa8" id="u7ad72aa8"><strong><span data-lake-id="u45d8b711" id="u45d8b711">​</span></strong><br></p>
  <p data-lake-id="u17270d78" id="u17270d78"><strong><span data-lake-id="u1a3dae85" id="u1a3dae85">所以，浮点数只是近似值，并不是精确值，所以不能用来表示金额。否则会有精度丢失。</span></strong></p>
  <h1 data-lake-id="N47vx" id="N47vx"><span data-lake-id="u55bd9747" id="u55bd9747">扩展知识</span></h1>
  <h3 data-lake-id="fb095b93" id="fb095b93"><span data-lake-id="u4a114487" id="u4a114487">十进制转二进制</span></h3>
  <p data-lake-id="u6a9654a6" id="u6a9654a6"><br></p>
  <p data-lake-id="u7b2abc0c" id="u7b2abc0c"><span data-lake-id="u6a9ebbc4" id="u6a9ebbc4">首先我们看一下，</span><strong><span data-lake-id="ua5c21618" id="ua5c21618">如何把十进制整数转换成二进制整数？</span></strong></p>
  <p data-lake-id="ua3653d0f" id="ua3653d0f"><br></p>
  <p data-lake-id="u1ff6bab8" id="u1ff6bab8"><span data-lake-id="ua5380b1b" id="ua5380b1b">十进制整数转换为二进制整数采用"除2取余，逆序排列"法。</span></p>
  <p data-lake-id="ubb3cc9a2" id="ubb3cc9a2"><br></p>
  <p data-lake-id="u6e432b2d" id="u6e432b2d"><span data-lake-id="u8d3432ec" id="u8d3432ec">具体做法是：</span></p>
  <p data-lake-id="u07861166" id="u07861166"><br></p>
  <ul list="u46b53e63">
   <li fid="u6a143dbb" data-lake-id="u16ab21a2" id="u16ab21a2"><span data-lake-id="u99901edf" id="u99901edf">用2整除十进制整数，可以得到一个商和余数；</span></li>
   <li fid="u6a143dbb" data-lake-id="u44dd6b54" id="u44dd6b54"><span data-lake-id="uc5dab408" id="uc5dab408">再用2去除商，又会得到一个商和余数，如此进行，直到商为小于1时为止</span></li>
   <li fid="u6a143dbb" data-lake-id="u0c4daf62" id="u0c4daf62"><span data-lake-id="u15bf71c4" id="u15bf71c4">然后把先得到的余数作为二进制数的低位有效位，后得到的余数作为二进制数的高位有效位，依次排列起来。</span></li>
  </ul>
  <p data-lake-id="u895641fe" id="u895641fe"><br></p>
  <p data-lake-id="u012b7233" id="u012b7233"><span data-lake-id="ua3357075" id="ua3357075">如，我们想要把127转换成二进制，做法如下：</span></p>
  <p data-lake-id="u02c0415f" id="u02c0415f"><br></p>
  <p data-lake-id="u52148af2" id="u52148af2"><img src="https://cdn.nlark.com/yuque/0/2022/jpeg/5378072/1668831261769-fc28aa7b-e539-428b-af3b-b03f772d2a6d.jpeg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_36%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"><span data-lake-id="ue23c8c75" id="ue23c8c75">​</span></p>
  <p data-lake-id="u11fa44fc" id="u11fa44fc"><br></p>
  <p data-lake-id="u3fa63b43" id="u3fa63b43"><span data-lake-id="ue2c9850e" id="ue2c9850e">那么，</span><strong><span data-lake-id="uf879189a" id="uf879189a">十进制小数转换成二进制小数，又该如何计算呢？</span></strong></p>
  <p data-lake-id="u8249def1" id="u8249def1"><br></p>
  <p data-lake-id="u1f645a76" id="u1f645a76"><span data-lake-id="uafb75b27" id="uafb75b27">十进制小数转换成二进制小数采用"乘2取整，顺序排列"法。</span></p>
  <p data-lake-id="ua25d5ec5" id="ua25d5ec5"><br></p>
  <p data-lake-id="uadb38700" id="uadb38700"><span data-lake-id="u6789eafc" id="u6789eafc">具体做法是：</span></p>
  <p data-lake-id="ud3a523c1" id="ud3a523c1"><br></p>
  <ul list="u780c7520">
   <li fid="u05666e90" data-lake-id="u12b6eaa5" id="u12b6eaa5"><span data-lake-id="u45583c6d" id="u45583c6d">用2乘十进制小数，可以得到积</span></li>
   <li fid="u05666e90" data-lake-id="u939e94a5" id="u939e94a5"><span data-lake-id="u74dd0a0c" id="u74dd0a0c">将积的整数部分取出，再用2乘余下的小数部分，又得到一个积</span></li>
   <li fid="u05666e90" data-lake-id="ub583f7c4" id="ub583f7c4"><span data-lake-id="u95066b53" id="u95066b53">再将积的整数部分取出，如此进行，直到积中的小数部分为零，此时0或1为二进制的最后一位。或者达到所要求的精度为止。</span></li>
  </ul>
  <p data-lake-id="ua8308ebc" id="ua8308ebc"><br></p>
  <p data-lake-id="u9d1973be" id="u9d1973be"><img src="https://cdn.nlark.com/yuque/0/2022/jpeg/5378072/1668831261784-23191a6f-9697-4e28-9c67-05286f3939b7.jpeg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_40%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"><span data-lake-id="u75c99143" id="u75c99143">​</span></p>
  <p data-lake-id="u5df0338c" id="u5df0338c"><br></p>
  <p data-lake-id="u3ee830a8" id="u3ee830a8"><span data-lake-id="u6963bec0" id="u6963bec0">所以，十进制的0.625对应的二进制就是0.101。</span></p>
  <p data-lake-id="uef51b3a3" id="uef51b3a3"><br></p>
  <h3 data-lake-id="ae65a6d4" id="ae65a6d4"><span data-lake-id="u51a988c9" id="u51a988c9">不是所有数都能用二进制表示</span></h3>
  <p data-lake-id="u2a614e17" id="u2a614e17"><br></p>
  <p data-lake-id="ub880c658" id="ub880c658"><span data-lake-id="u703a9961" id="u703a9961">我们知道了如何将一个十进制小数转换成二进制，那么是不是计算就可以直接用二进制表示小数了呢？</span></p>
  <p data-lake-id="u055c1d03" id="u055c1d03"><br></p>
  <p data-lake-id="u9458549c" id="u9458549c"><span data-lake-id="u3cb1b663" id="u3cb1b663">前面我们的例子中0.625是一个特列，那么还是用同样的算法，请计算下0.1对应的二进制是多少？</span></p>
  <p data-lake-id="u018498f2" id="u018498f2"><br></p>
  <p data-lake-id="u4fae05f9" id="u4fae05f9"><img src="https://cdn.nlark.com/yuque/0/2022/jpeg/5378072/1668831261776-8d3d001c-341d-458f-bcc2-75294ea4d8ab.jpeg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_52%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"><span data-lake-id="u5af8e288" id="u5af8e288">​</span></p>
  <p data-lake-id="u20b1aea5" id="u20b1aea5"><br></p>
  <p data-lake-id="u7b41232c" id="u7b41232c"><span data-lake-id="u67f34867" id="u67f34867">我们发现，0.1的二进制表示中出现了无限循环的情况，也就是(0.1)10 = (0.000110011001100…)2</span></p>
  <p data-lake-id="u459bc6d9" id="u459bc6d9"><br></p>
  <p data-lake-id="u6ddb2f8b" id="u6ddb2f8b"><span data-lake-id="u15e4d54b" id="u15e4d54b">这种情况，计算机就没办法用二进制精确的表示0.1了。</span></p>
  <p data-lake-id="u43b597a0" id="u43b597a0"><br></p>
  <p data-lake-id="u04e62e03" id="u04e62e03"><strong><span data-lake-id="ued6d60ce" id="ued6d60ce">也就是说，对于像0.1这种数字，我们是没办法将他转换成一个确定的二进制数的。</span></strong></p>
  <p data-lake-id="u77930904" id="u77930904"><br></p>
  <h3 data-lake-id="776e1df9" id="776e1df9"><span data-lake-id="u0a54291b" id="u0a54291b">IEEE 754</span></h3>
  <p data-lake-id="uf36ecb5b" id="uf36ecb5b"><br></p>
  <p data-lake-id="u0d96b188" id="u0d96b188"><span data-lake-id="u0d262a6d" id="u0d262a6d">为了解决部分小数无法使用二进制精确表示的问题，于是就有了IEEE 754规范。</span></p>
  <p data-lake-id="ub0be28af" id="ub0be28af"><br></p>
  <p data-lake-id="u23dada2f" id="u23dada2f"><span data-lake-id="u623f8ac8" id="u623f8ac8">IEEE二进制浮点数算术标准（IEEE 754）是20世纪80年代以来最广泛使用的浮点数运算标准，为许多CPU与浮点运算器所采用。</span></p>
  <p data-lake-id="u58fd8171" id="u58fd8171"><br></p>
  <blockquote data-lake-id="uaacf1b8d" id="uaacf1b8d">
   <p data-lake-id="u9786256c" id="u9786256c"><span data-lake-id="uba2bea41" id="uba2bea41">浮点数和小数并不是完全一样的，计算机中小数的表示法，其实有定点和浮点两种。因为在位数相同的情况下，定点数的表示范围要比浮点数小。所以在计算机科学中，使用浮点数来表示实数的近似值。</span></p>
  </blockquote>
  <p data-lake-id="u4d178a01" id="u4d178a01"><br></p>
  <p data-lake-id="u874383d4" id="u874383d4"><span data-lake-id="ubb3f704d" id="ubb3f704d">IEEE 754规定了四种表示浮点数值的方式：单精确度（32位）、双精确度（64位）、延伸单精确度（43比特以上，很少使用）与延伸双精确度（79比特以上，通常以80位实现）。</span></p>
  <p data-lake-id="uc5b3278b" id="uc5b3278b"><br></p>
  <p data-lake-id="ud553e08a" id="ud553e08a"><span data-lake-id="u37e36f95" id="u37e36f95">其中最常用的就是32位单精度浮点数和64位双精度浮点数。</span></p>
  <p data-lake-id="u2fad2481" id="u2fad2481"><br></p>
  <p data-lake-id="uf6116c64" id="uf6116c64"><strong><span data-lake-id="ue2e118b4" id="ue2e118b4">IEEE并没有解决小数无法精确表示的问题，只是提出了一种使用近似值表示小数的方式，并且引入了精度的概念。</span></strong></p>
  <p data-lake-id="ue27f1465" id="ue27f1465"><br></p>
  <p data-lake-id="u927d6202" id="u927d6202"><span data-lake-id="uce1cadbe" id="uce1cadbe">浮点数是一串0和1构成的位序列(bit sequence)，从逻辑上用三元组{S,E,M}表示一个数N,如下图所示：</span></p>
  <p data-lake-id="ude5b04c3" id="ude5b04c3"><br></p>
  <p data-lake-id="ued583b73" id="ued583b73"><img src="https://cdn.nlark.com/yuque/0/2022/jpeg/5378072/1668831261781-ee0b02d3-5e60-4c84-94fb-1ed67fdd848a.jpeg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_28%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"><span data-lake-id="u8bc6d7c9" id="u8bc6d7c9">​</span></p>
  <p data-lake-id="u45b7f76a" id="u45b7f76a"><br></p>
  <ul list="udb5edd6b">
   <li fid="ufc0f2c92" data-lake-id="uce516fba" id="uce516fba"><span data-lake-id="ubf3ccc74" id="ubf3ccc74">S(sign)表示N的符号位。对应值s满足：n&gt;0时，s=0; n≤0时，s=1。</span></li>
   <li fid="ufc0f2c92" data-lake-id="u1119a980" id="u1119a980"><span data-lake-id="u34e7fca5" id="u34e7fca5">E(exponent)表示N的指数位，位于S和M之间的若干位。对应值e值也可正可负。</span></li>
   <li fid="ufc0f2c92" data-lake-id="uf832c28f" id="uf832c28f"><span data-lake-id="uc82a0c34" id="uc82a0c34">M(mantissa)表示N的尾数位，恰好，它位于N末尾。M也叫有效数字位（significand）、系数位（coefficient）, 甚至被称作"小数"。</span></li>
  </ul>
  <p data-lake-id="u903d3fff" id="u903d3fff"><br></p>
  <p data-lake-id="u53d33160" id="u53d33160"><span data-lake-id="u64170b7c" id="u64170b7c">则浮点数N的实际值n由下方的式子表示：</span></p>
  <p data-lake-id="u1b3aa779" id="u1b3aa779"><br></p>
  <p data-lake-id="ubb1517f2" id="ubb1517f2"><img src="https://cdn.nlark.com/yuque/0/2022/jpeg/5378072/1668831261782-f870cc7f-af15-4bf3-abef-32f18af1d72c.jpeg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_11%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"><span data-lake-id="u62cc0d8b" id="u62cc0d8b">​</span></p>
  <p data-lake-id="ud668ef7e" id="ud668ef7e"><br></p>
  <p data-lake-id="u09754333" id="u09754333"><span data-lake-id="ub346d3d9" id="ub346d3d9">上面这个公式看起来很复杂，其中符号位和尾数位还比较容易理解，但是这个指数位就不是那么容易理解了。</span></p>
  <p data-lake-id="u737eb05c" id="u737eb05c"><br></p>
  <p data-lake-id="u819ca96c" id="u819ca96c"><span data-lake-id="u3b033826" id="u3b033826">其实，大家也不用太过于纠结这个公式，大家只需要知道对于单精度浮点数，最多只能用32位字符表示一个数字，双精度浮点数最多只能用64位来表示一个数字。</span></p>
  <p data-lake-id="u71a99b8d" id="u71a99b8d"><br></p>
  <p data-lake-id="ubb98d816" id="ubb98d816"><strong><span data-lake-id="u8bd59933" id="u8bd59933">而对于那些无限循环的二进制数来说，计算机采用浮点数的方式保留了一定的有效数字，那么这个值只能是近似值，不可能是真实值。</span></strong></p>
  <p data-lake-id="ue0d1f4c4" id="ue0d1f4c4"><br></p>
  <p data-lake-id="ua54be198" id="ua54be198"><span data-lake-id="u2b100758" id="u2b100758">至于一个数对应的IEEE 754浮点数应该如何计算，不是本文的重点，这里就不再赘述了，过程还是比较复杂的，需要进行对阶、尾数求和、规格化、舍入以及溢出判断等。</span></p>
  <p data-lake-id="u4ec18ecb" id="u4ec18ecb"><br></p>
  <p data-lake-id="u130721db" id="u130721db"><span data-lake-id="u5486dcb2" id="u5486dcb2">但是这些其实不需要了解的太详细，我们只需要知道，小数在计算机中的表示是近似数，并不是真实值。根据精度不同，近似程度也有所不同。</span></p>
  <p data-lake-id="ufd9ee409" id="ufd9ee409"><br></p>
  <p data-lake-id="uc70713b5" id="uc70713b5"><span data-lake-id="ub99e9292" id="ub99e9292">如0.1这个小数，他对应的在双精度浮点数的二进制为：0.00011001100110011001100110011001100110011001100110011001 。</span></p>
  <p data-lake-id="u85856b46" id="u85856b46"><br></p>
  <p data-lake-id="uc712d38d" id="uc712d38d"><span data-lake-id="u680539fc" id="u680539fc">0.2这个小数0.00110011001100110011001100110011001100110011001100110011 。</span></p>
  <p data-lake-id="ub33da7a7" id="ub33da7a7"><br></p>
  <p data-lake-id="u82fa9de9" id="u82fa9de9"><span data-lake-id="u04e31e21" id="u04e31e21">所以两者相加：</span></p>
  <p data-lake-id="uc472b5d7" id="uc472b5d7"><br></p>
  <p data-lake-id="u8e3fbdcc" id="u8e3fbdcc"><img src="https://cdn.nlark.com/yuque/0/2022/jpeg/5378072/1668831262016-d222a4ac-7301-4ce3-a6f8-14d3a87602df.jpeg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_32%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"><span data-lake-id="u021f3796" id="u021f3796">​</span></p>
  <p data-lake-id="u3c5adbb1" id="u3c5adbb1"><br></p>
  <p data-lake-id="udd3e5f41" id="udd3e5f41"><span data-lake-id="u375eb4ea" id="u375eb4ea">转换成10进制之后得到：0.30000000000000004！</span></p>
  <p data-lake-id="u8148b3d9" id="u8148b3d9"><br></p>
  <h3 data-lake-id="b340a97b" id="b340a97b"><span data-lake-id="uf352c137" id="uf352c137">避免精度丢失</span></h3>
  <p data-lake-id="uae806a27" id="uae806a27"><br></p>
  <p data-lake-id="ua3551ac7" id="ua3551ac7"><span data-lake-id="u59d7885c" id="u59d7885c">在Java中，使用float表示单精度浮点数，double表示双精度浮点数，表示的都是近似值。</span></p>
  <p data-lake-id="ud3c4bccf" id="ud3c4bccf"><br></p>
  <p data-lake-id="u8b24bcdd" id="u8b24bcdd"><span data-lake-id="udf7cd877" id="udf7cd877">所以，在Java代码中，千万不要使用float或者double来进行高精度运算，尤其是金额运算，否则就很容易产生资损问题。</span></p>
  <p data-lake-id="u075cd4aa" id="u075cd4aa"><br></p>
  <p data-lake-id="u422af3b9" id="u422af3b9"><span data-lake-id="u2907627b" id="u2907627b">为了解决这样的精度问题，Java中提供了BigDecimal来进行精确运算。</span></p>
 </body>
</html>